package src.ee.ioc.smaug.world.beings;

import java.util.Random;

import src.ee.ioc.smaug.fsm.*;

import src.ee.ioc.smaug.world.*;

/**
 * Individual for storing Moore machine with given maximal number of states
 * 
 * @author Margarita Spichakova
 * 
 */
public class MooreSMMaxNumofStates extends Individual {
    int sizeAlpha = Enviroment.inAlphabet.getSizeOfAlphabet();
    int bitsPerTransition = countBitsPerTransition();

    int bitsPerState = countBitsPerState();

    int bitPerSection = bitsPerTransition + 1 + sizeAlpha * bitsPerState;
    
    /**
     * Constructor
     */
    public MooreSMMaxNumofStates() {
        fenotype = new MooreMachine();
        define();
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#generateInBinaryForm()
     */
    public void generateInBinaryForm() {


        genotype = new boolean[bitPerSection * Enviroment.numberOfStates];
        for (int i = 0; i < bitPerSection * Enviroment.numberOfStates; i++) {
            genotype[i] = Coin.flipCoin();

        }
        while (isDead()) {
            for (int i = 0; i < bitPerSection * Enviroment.numberOfStates; i++) {
                genotype[i] = Coin.flipCoin();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#generateInDecinalForm()
     */
    public void generateInDecinalForm() {

        int memPerState = 1 + 1 + sizeAlpha;
        genotypeDec = new int[Enviroment.numberOfStates * memPerState];

        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            Random rnd = new Random();
            genotypeDec[i * memPerState] = rnd.nextInt(Enviroment.outAlphabet
                    .getSizeOfAlphabet());

            if (i == 0) {
                genotypeDec[i * memPerState + 1] = 1;
            } else {
                int prob = rnd.nextInt();
                if (prob <= 0.1) {
                    genotypeDec[i * memPerState + 1] = 0;
                } else
                    genotypeDec[i * memPerState + 1] = 1;

            }

            for (int j = 0; j < sizeAlpha; j++) {
                genotypeDec[i * memPerState + j + 1 + 1] = rnd
                        .nextInt(Enviroment.numberOfStates);
            }
        }

        genotype = new boolean[bitPerSection * Enviroment.numberOfStates];

        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            String output = Integer
                    .toBinaryString(genotypeDec[i * memPerState]);

            int addZeros = bitsPerTransition - output.length();

            for (int k = 0; k < addZeros; k++) {
                genotype[i * bitPerSection + k] = false;

            }
            for (int k = addZeros; k < bitsPerTransition; k++) {

                if (output.charAt(k - addZeros) == '0') {
                    genotype[i * bitPerSection + k] = false;
                } else
                    genotype[i * bitPerSection + k] = true;

            }

            if (genotypeDec[i * memPerState + 1] == 0) {
                genotype[i * bitPerSection + bitsPerTransition] = false;
            } else {
                genotype[i * bitPerSection + bitsPerTransition] = true;
            }

            for (int j = 0; j < sizeAlpha; j++) {
                String state = Integer.toBinaryString(genotypeDec[i
                        * memPerState + j + 1 + 1]);

                addZeros = bitsPerState - state.length();

                for (int k = 0; k < addZeros; k++) {

                    genotype[i * (bitPerSection) + j * bitsPerState + k
                            + bitsPerTransition + 1] = false;

                }
                for (int k = addZeros; k < bitsPerState; k++) {

                    if (state.charAt(k - addZeros) == '0') {
                        genotype[i * (bitPerSection) + j * bitsPerState + k
                                + bitsPerTransition + 1] = false;

                    } else
                        genotype[i * (bitPerSection) + j * bitsPerState + k
                                + bitsPerTransition + 1] = true;

                }

            }

        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#define()
     */
    public void define() {
        genotype = new boolean[bitPerSection * Enviroment.numberOfStates];
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#isDead()
     */
    public boolean isDead() {
        boolean dead = false;
        decodeBinToDec();
        int alive = 0;
        int memPerState = 1 + 1 + Enviroment.inAlphabet.getSizeOfAlphabet();
        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            if (genotypeDec[1] == 0) {
                dead = true;
            }
            if (genotypeDec[i * memPerState + 1] == 1) {
                alive++;
            }

        }
        if (alive < Enviroment.numberOfStates / 2 + 1) {
            dead = true;
        }
        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            if (genotypeDec[i * memPerState] >= Enviroment.outAlphabet
                    .getSizeOfAlphabet()) {
                dead = true;
            }
        }
        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            for (int j = 0; j < Enviroment.inAlphabet.getSizeOfAlphabet(); j++) {
                if (genotypeDec[i * memPerState + j + 1 + 1] >= Enviroment.numberOfStates) {
                    dead = true;
                }
            }
        }
        return dead;
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decode()
     */
    public void decode() {
        decodeBinToDec();
        decodeDecToFSM();
        this.genotypeDec = null;

    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decodeBinToDec()
     */
    public void decodeBinToDec() {
        int memPerState = 1 + 1 + Enviroment.inAlphabet.getSizeOfAlphabet();
        genotypeDec = new int[Enviroment.numberOfStates * memPerState];

        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            String s = "";
            for (int j = 0; j < bitsPerTransition; j++) {
                s += Coin.booleanToInteger(genotype[i * (bitPerSection) + j]);
            }
            genotypeDec[i * memPerState] = Integer.parseInt(s, 2);

            s = new String("");

            s += Coin.booleanToInteger(genotype[i * (bitPerSection) + 1]);
            genotypeDec[i * memPerState + 1] = Integer.parseInt(s, 2);
            s = new String("");

            for (int j = 0; j < sizeAlpha; j++) {
                for (int k = 0; k < bitsPerState; k++) {
                    s += Coin.booleanToInteger(genotype[i * (bitPerSection) + j
                            * bitsPerState + k + bitsPerTransition + 1]);
                }

                genotypeDec[i * memPerState + j + 1 + 1] = Integer.parseInt(s,
                        2);
                s = new String("");
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see src.ee.ioc.smaug.world.beings.Individual#decodeDecToFSM()
     */
    public void decodeDecToFSM() {
        fenotype = new MooreMachine();
        int memPerState = 1 + 1 + Enviroment.inAlphabet.getSizeOfAlphabet();
        fenotype.setInputAlphabet(Enviroment.inAlphabet);
        fenotype.setOutputAlphabet(Enviroment.outAlphabet);
        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            State q = new State(i, Enviroment.outAlphabet
                    .returnSymbolAtPosition(genotypeDec[i * memPerState]),
                    false, false);
            if (i == 0) {
                q.setInitialState(true);
            }
            fenotype.addState(q);
            if (genotypeDec[i * memPerState + 1] == 0) {
                q.setActive(false);
            }

        }
        fenotype.setBounds();

        for (int i = 0; i < Enviroment.numberOfStates; i++) {
            State fromState = this.fenotype.getState(i);
            for (int j = 0; j < Enviroment.inAlphabet.getSizeOfAlphabet(); j++) {
                State toState = this.fenotype.getState(genotypeDec[i
                        * memPerState + j + 1 + 1]);
                char inChar = this.fenotype.getInputAlphabet()
                        .returnSymbolAtPosition(j);
                Transition qToQRandChar = new Transition(fromState, toState,
                        inChar, '-');
                fenotype.addTransition(qToQRandChar);
            }
        }

    }

    /**
     * @return number of states required to store one state
     */
    public int countBitsPerState() {
        int bitsPerState = (int) Math.round(Math.log(Enviroment.numberOfStates)
                / Math.log(2));
        if (Enviroment.numberOfStates <= Math.pow(2, bitsPerState)) {
            return bitsPerState;
        } else
            return bitsPerState + 1;

    }

    /**
     * @return number of states required to store transition
     */
    public int countBitsPerTransition() {
        int sizeBeta = Enviroment.outAlphabet.getSizeOfAlphabet();
        int bitsPerOutput = (int) Math.round(Math.log(sizeBeta) / Math.log(2));
        if (sizeBeta <= Math.pow(2, bitsPerOutput)) {
            return bitsPerOutput;
        } else
            return bitsPerOutput + 1;

    }
}
